package com.lanhao.aot.aliyun;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.ObjectMetadata;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;

/**
 * 示例说明
 * 
 * HelloOSS是OSS Java
 * SDK的示例程序，您可以修改endpoint、accessKeyId、accessKeySecret、bucketName后直接运行。
 * 运行方法请参考README。
 * 
 * 本示例中的并不包括OSS Java SDK的所有功能，详细功能及使用方法，请参看“SDK手册 > Java-SDK”，
 * 链接地址是：https://help.aliyun.com/document_detail/oss/sdk/java-sdk/preface.html?spm=5176.docoss/sdk/java-sdk/。
 * 
 * 调用OSS Java SDK的方法时，抛出异常表示有错误发生；没有抛出异常表示成功执行。 当错误发生时，OSS Java
 * SDK的方法会抛出异常，异常中包括错误码、错误信息，详细请参看“SDK手册 > Java-SDK > 异常处理”，
 * 链接地址是：https://help.aliyun.com/document_detail/oss/sdk/java-sdk/exception.html?spm=5176.docoss/api-reference/error-response。
 * 
 * OSS控制台可以直观的看到您调用OSS Java
 * SDK的结果，OSS控制台地址是：https://oss.console.aliyun.com/index#/。
 * OSS控制台使用方法请参看文档中心的“控制台用户指南”，
 * 指南的来链接地址是：https://help.aliyun.com/document_detail/oss/getting-started/get-started.html?spm=5176.docoss/user_guide。
 * 
 * OSS的文档中心地址是：https://help.aliyun.com/document_detail/oss/user_guide/overview.html。
 * OSS Java
 * SDK的文档地址是：https://help.aliyun.com/document_detail/oss/sdk/java-sdk/install.html?spm=5176.docoss/sdk/java-sdk。
 * 
 */
public class AliyunOssUtil {
	private final static Logger LOG = LoggerFactory.getLogger(AliyunOssUtil.class);
	public static String RES_OSS_DOMAIN = "https://res-oss.anhelogistics.com";
	// endpoint是访问OSS的域名。如果您已经在OSS的控制台上 创建了Bucket，请在控制台上查看域名。
	private static String endpoint = "http://oss-cn-hangzhou.aliyuncs.com";
	// accessKeyId和accessKeySecret是OSS的访问密钥，您可以在控制台上创建和查看，
	private static String accessKeyId = "LTAI5t9LtFtB5wdVKNBaoC1e";
	private static String accessKeySecret = "D97rN1SKBYUFu5NG3m3Fr2vLJbZY4q";
	// Bucket用来管理所存储Object的存储空间，详细描述请参看“开发人员指南 > 基本概念 > OSS基本概念介绍”。
	private static String bucketName = "anhelogistics";

	public interface LABEL_FORM {
		String URL = "URL"; // URL下载，以文件流的形式
		String BYTE = "BYTE"; // URL下载，以字节数组的形式
		String BASE64 = "BASE64"; // 文件Base64格式
	}

	public interface OSS_FOLDER {
		String SURVEY = "survey"; // 勘察文件
		String E_LABEL = "eLabel"; // 电子标签
	}

	/**
	 * 上传到OSS服务器
	 * 
	 * @param instream 文件流
	 * @param fileName 文件名称 包括后缀名，可包含多级目录，以 / 开头
	 * @return 出错返回"" 文件可访问路径
	 */
	public static String uploadFile2OSS(InputStream instream, String fileName, String fileFolder) {
		OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
		try {
			// 判断bucket是否已经存在,不存在进行创建
			if (ossClient.doesBucketExist(bucketName)) {
				LOG.info("您已经创建Bucket：{}", bucketName);
			} else {
				LOG.info("您的Bucket不存在，创建Bucket：{}", bucketName);
				ossClient.createBucket(bucketName);
			}
			// 创建上传Object的Metadata
			ObjectMetadata objectMetadata = new ObjectMetadata();
			objectMetadata.setContentLength(instream.available());
			objectMetadata.setCacheControl("no-cache");
			objectMetadata.setHeader("Pragma", "no-cache");
			objectMetadata.setContentType(getFileContentType(fileName.substring(fileName.lastIndexOf(".") + 1)));
			objectMetadata.setContentDisposition("inline;filename=" + fileName);

			// 指定上传文件操作时是否覆盖同名Object。
			// 不指定x-oss-forbid-overwrite时，默认覆盖同名Object。
			// 指定x-oss-forbid-overwrite为false时，表示允许覆盖同名Object。
			// 指定x-oss-forbid-overwrite为true时，表示禁止覆盖同名Object，如果同名Object已存在，程序将报错。
			objectMetadata.setHeader("x-oss-forbid-overwrite", "false");
			String objectName = fileFolder + fileName;
			// 上传文件
			ossClient.putObject(bucketName, objectName, instream, objectMetadata);
			// 封装 url 路径
			return RES_OSS_DOMAIN + "/" + objectName;
		} catch (IOException e) {
			e.printStackTrace();
			LOG.error(e.getMessage(), e);
		} finally {
			ossClient.shutdown();
			try {
				if (instream != null) {
					instream.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return StringUtils.EMPTY;
	}

	/**
	 * 根据不同文件类型设置contentType
	 */
	private static String getFileContentType(String ext) {
		String lext = ext.toLowerCase();
		String contentType = null;
		switch (lext) {
		case "html":
		case "htm":
			contentType = "text/html; charset=utf-8";
			break;
		case "txt":
			contentType = "text/plain; charset=utf-8";
			break;
		case "pdf":
			contentType = "application/pdf";
			break;
		case "doc":
			contentType = "application/msword";
			break;
		case "docx":
			contentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
			break;
		case "xls":
			contentType = "application/vnd.ms-excel";
			break;
		case "xlsx":
			contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
			break;
		case "ppt":
			contentType = "application/vnd.ms-powerpoint";
			break;
		case "pptx":
			contentType = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
			break;
		case "wps":
			contentType = "application/vnd.ms-works";
			break;
		case "xml":
			contentType = "application/xml; charset=utf-8";
			break;
		case "gif":
			contentType = "image/gif";
			break;
		case "jpg":
		case "jpeg":
			contentType = "image/jpeg";
			break;
		case "png":
			contentType = "image/png";
			break;
		case "zip":
			contentType = "application/zip";
			break;
		case "tar":
			contentType = "application/x-tar";
			break;
		case "gz":
			contentType = "application/gzip";
			break;
		case "mp3":
			contentType = "audio/mpeg";
			break;
		case "mp4":
			contentType = "video/mp4";
			break;
		case "avi":
			contentType = "video/avi";
			break;
		case "flv":
			contentType = "video/x-flv";
			break;
		case "md":
			contentType = "text/markdown";
			break;
		case "rp":
			contentType = "application/x-rp";
			break;
		}
		return contentType;
	}

	/**
	 * 适用于同步场景，无需关注上传路径
	 * 
	 * @param instream
	 * @param carrier
	 * @param fileName
	 * @return
	 */
	public static String uploadLabelFile2OSS(InputStream instream, String carrier, String fileName,
			String fullFilePath) {
		// 如果没指定全路径，那么就默认生成一个
		if (StringUtils.isBlank(fullFilePath)) {
			fullFilePath = "/" + carrier + "/" + datePath() + "/" + fileName;
		}
		return uploadFile2OSS(instream, fullFilePath, OSS_FOLDER.E_LABEL);
	}

	/**
	 * 适用于异步场景，可提前预知上传路径的
	 * 
	 * @param instream
	 * @param fileFullPath
	 * @return
	 */
	public static String uploadLabelFile2OSSWithFullPath(InputStream instream, String fileFullPath) {
		return uploadFile2OSS(instream, fileFullPath, OSS_FOLDER.E_LABEL);
	}

	public static String uploadLabelFile2OSS(File tmpFile, String fileFullPath) {
		try {
			InputStream stream = new FileInputStream(tmpFile);
			String labelOssUrl = uploadFile2OSS(stream, fileFullPath, OSS_FOLDER.E_LABEL);
			if (StringUtils.isNotBlank(labelOssUrl)) {
				boolean r = tmpFile.delete();
				LOG.info("将标签文件上传到OSS成功，返回OSSUrl={}，删除临时文件r={}", labelOssUrl, r);
				return labelOssUrl;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return StringUtils.EMPTY;
	}

	public static String uploadLabel2OSS(String fileName, String fullFilePath, String carrier, String fileForm,
			String value) {
		try {
			if (StrUtil.equalsAny(fileForm, LABEL_FORM.URL, LABEL_FORM.BYTE)) {
				// 如果是文件URL的形式，那么需要存到本地临时文件，然后再上传
				File tmpFile = new File("/tmp/" + fileName);
				if (StrUtil.equals(fileForm, LABEL_FORM.URL)) {
					long s = HttpUtil.downloadFile(value, tmpFile);
					LOG.info("下载文件流信息，大小s={}", s);
				}
				if (StrUtil.equals(fileForm, LABEL_FORM.BYTE)) {
					byte[] fileByte = HttpUtil.downloadBytes(value);
					FileUtils.writeByteArrayToFile(tmpFile, fileByte);
				}
				InputStream stream = new FileInputStream(tmpFile);
				String labelOssUrl = AliyunOssUtil.uploadLabelFile2OSS(stream, carrier, fileName, fullFilePath);
				if (StringUtils.isNotBlank(labelOssUrl)) {
					boolean r = tmpFile.delete();
					LOG.info("将{}标签文件={}上传到OSS成功， 返回OSSUrl={}，删除临时文件r={}", carrier, value, labelOssUrl, r);
					return labelOssUrl;
				}
			} else if (LABEL_FORM.BASE64.equals(fileForm)) {
				// 如果是文件Base64的形式，那么需要解码后转换为字节流，然后再上传
				ByteArrayInputStream stream = IoUtil.toStream(Base64.decode(value));
				String labelOssUrl = AliyunOssUtil.uploadLabelFile2OSS(stream, carrier, fileName, fullFilePath);
				return labelOssUrl;
			}
		} catch (Exception e) {
			e.printStackTrace();
			LOG.info("文件上传到OSS平台失败，fileName={}，原因e={}", fileName, e.getMessage());
		}
		return StringUtils.EMPTY;
	}

	public static Pair<String, String> genFullPathAndVisitUrl(String fileName, String carrier) {
		String fileFullPath = "/" + carrier + "/" + datePath() + "/" + fileName;
		String visitUrl = AliyunOssUtil.RES_OSS_DOMAIN + "/" + OSS_FOLDER.E_LABEL + fileFullPath;
		return Pair.of(fileFullPath, visitUrl);
	}

	public static final String datePath() {
		Date now = new Date();
		return DateFormatUtils.format(now, "yyyy/MM/dd");
	}

	public static void main(String[] args) {
	}

}
